﻿using System;
using System.Collections.Generic;
using System.Reflection;
using MessageSystem.Managed.Interface;

namespace MessageSystem.Managed.Rpc
{
    public class RpcDomain:RpcHandler
    {        
        public Action<RpcDomain>                    OnDomainLost { get; set; }
        public RpcAddress                           Address { get; private set; }
        private Dictionary<int, RpcService>         _services;

        public RpcDomain(IMessageChannel mc, RpcAddress address)
            :base(mc)
        {
            _services = new Dictionary<int, RpcService>(new Int32EqualityComparer());
            Address = address;
        }

        public RpcBinder BindService<Proxy, Interface>(int sessionid, object serviceImpl, Action onLost) where Proxy : RpcProxy
        {
            if ((serviceImpl is Interface))
            {
                Type proxyType = typeof(Proxy);
                MethodInfo getInvokerMethod = proxyType.GetMethod("GetRpcInvoker", BindingFlags.Static | BindingFlags.NonPublic);
                RpcInvoker invoker = getInvokerMethod.Invoke(null, null) as RpcInvoker;
                MethodInfo getServiceTypeMethod = proxyType.GetMethod("GetServiceType", BindingFlags.Static | BindingFlags.NonPublic);
                Type serviceType = getServiceTypeMethod.Invoke(null, null) as Type;

                if (serviceType == typeof(Interface))
                {
                    Int16 serviceId = RpcControl.GetServiceId(serviceType);
                    return BindService(serviceId, sessionid, invoker, serviceImpl, onLost);
                }
                else
                {
                    throw new Exception("Proxy mismatch Interface");
                }
            }
            else
            {
                throw new Exception("serviceImpl doesn't implements Interface");
            }
        }

        public RpcBinder BindService(Int16 serviceId, int sessionId, RpcInvoker invoker, object serviceImpl, Action onLost)
        {           
            RpcFactory.Add(serviceId, invoker);
            
            RpcService service = CacheService(serviceId);
            RpcSessionService session = service.Bind(sessionId, serviceImpl, onLost);
            return new RpcBinder(session);
        }

        public Proxy CreateProxy<Proxy>(int sessionid,Action onLost) where Proxy : RpcProxy
        {
            Type proxyType = typeof(Proxy);
            MethodInfo getInvokerMethod = proxyType.GetMethod("GetRpcInvoker", BindingFlags.Static | BindingFlags.NonPublic);
            RpcInvoker invoker = getInvokerMethod.Invoke(null, null) as RpcInvoker;
            MethodInfo getServiceTypeMethod = proxyType.GetMethod("GetServiceType", BindingFlags.Static | BindingFlags.NonPublic);
            Type serviceType = getServiceTypeMethod.Invoke(null, null) as Type;

            Int16 serviceId = RpcControl.GetServiceId(serviceType);
            return CreateProxy<Proxy>(serviceId, sessionid, invoker, onLost);
        }

        public Proxy CreateProxy<Proxy>(Int16 serviceId,int sessionid,RpcInvoker invoker,Action onLost) where Proxy : RpcProxy
        {
            Type proxyType = typeof(Proxy);

            RpcFactory.Add(serviceId, invoker);
            RpcService service = CacheService(serviceId);
            RpcSessionService session = service.Bind(sessionid, null, onLost);
            Proxy proxy = Activator.CreateInstance(proxyType, this,sessionid,_broadCaster, session) as Proxy;
            return proxy;
        }

        private RpcService CacheService(Int16 serviceId)
        {
            RpcService service = null;
            if (!_services.TryGetValue(serviceId, out service))
            {
                service = new RpcService(serviceId, this);
                _services[serviceId] = service;
            }
            return service;
        }

        protected override RpcService GetService(int serviceid) 
        {
            RpcService service = null;
            _services.TryGetValue(serviceid, out service);
            return service;
        }

        public override void OnLost() 
        {
            base.OnLost();

            foreach (RpcService service in _services.Values)
            {
                service.Lost();
            }

            if (null != OnDomainLost)
            {
                OnDomainLost(this);
            }

            OnDomainLost = null;
            _services = null;
        }
    }
}
